import type { CSSProperties } from "vue";
import type { TriggerPopupTranslate } from "./interface";
import { TriggerPosition } from "@/ArcoUni/utils/interface";
import { isArray } from "@/utils/is";

interface ScrollRect {
  top: number;
  bottom: number;
  left: number;
  right: number;
  scrollTop: number;
  scrollBottom: number;
  scrollLeft: number;
  scrollRight: number;
  width: number;
  height: number;
}

export const getElementScrollRect = (
  element: HTMLElement,
  containerRect: DOMRect,
) => {
  const rect = element.getBoundingClientRect();

  return {
    top: rect.top,
    bottom: rect.bottom,
    left: rect.left,
    right: rect.right,
    scrollTop: rect.top - containerRect.top,
    scrollBottom: rect.bottom - containerRect.top,
    scrollLeft: rect.left - containerRect.left,
    scrollRight: rect.right - containerRect.left,
    width: element.offsetWidth ?? element.clientWidth,
    height: element.offsetHeight ?? element.clientHeight,
  };
};

interface PositionOffset {
  left: number;
  top: number;
}

const getPopupOffset = (
  position: TriggerPosition,
  triggerRect: ScrollRect,
  popupRect: ScrollRect,
  {
    offset = 0,
    translate = [0, 0],
  }: {
    offset?: number;
    translate?: TriggerPopupTranslate;
  } = {},
): PositionOffset => {
  // prettier-ignore
  const _translate = (isArray(translate) ? translate : translate[position]) ?? [0, 0];

  switch (position) {
    case "top":
      return {
        left:
          (Math.round(triggerRect.width) - Math.round(popupRect.width)) / 2 +
          _translate[0],
        top: -popupRect.height - offset + _translate[1],
      };
    case "tl":
      return {
        left: _translate[0],
        top: -popupRect.height - offset + _translate[1],
      };
    case "tr":
      return {
        left: triggerRect.width - popupRect.width + _translate[0],
        top: -popupRect.height - offset + _translate[1],
      };
    case "bottom":
      return {
        left:
          (Math.round(triggerRect.width) - Math.round(popupRect.width)) / 2 +
          _translate[0],
        top: triggerRect.height + offset + _translate[1],
      };
    case "bl":
      return {
        left: _translate[0],
        top: triggerRect.height + offset + _translate[1],
      };
    case "br":
      return {
        left: -popupRect.width + triggerRect.width + _translate[0],
        top: triggerRect.height + offset + _translate[1],
      };
    case "left":
      return {
        left: -(popupRect.width + offset + _translate[0]),
        top:
          (Math.round(triggerRect.height) - Math.round(popupRect.height)) / 2 +
          _translate[1],
      };
    case "lt":
      return {
        left: -(popupRect.width + offset + _translate[0]),
        top: 0 + _translate[1],
      };
    case "lb":
      return {
        left: -popupRect.width - offset + _translate[0],
        top: -(popupRect.height - triggerRect.height) + _translate[1],
      };
    case "right":
      return {
        left: triggerRect.width + offset + _translate[0],
        top:
          (Math.round(triggerRect.height) - Math.round(popupRect.height)) / 2 +
          _translate[1],
      };
    case "rt":
      return {
        left: triggerRect.width + offset + _translate[0],
        top: _translate[1],
      };
    case "rb":
      return {
        left: triggerRect.width + offset + _translate[0],
        top: -(popupRect.height - triggerRect.height) + _translate[1],
      };
    default:
      return {
        left: 0,
        top: 0,
      };
  }
};

export const getPopupStyle = (
  position: TriggerPosition,
  containerRect: ScrollRect,
  popupRect: ScrollRect,
  {
    offset = 0,
    translate = [0, 0],
    customStyle = {},
  }: {
    offset?: number;
    translate?: TriggerPopupTranslate;
    customStyle?: CSSProperties;
    autoFitPosition?: boolean;
  } = {},
): { style: CSSProperties; position: TriggerPosition } => {
  const finalPosition = position;
  const popupPosition = getPopupOffset(position, containerRect, popupRect, {
    offset,
    translate,
  });

  const style = {
    left: `${popupPosition.left}px`,
    top: `${popupPosition.top}px`,
    ...customStyle,
  };

  return {
    style,
    position: finalPosition,
  };
};

export const getArrowStyle = (
  position: TriggerPosition,
  triggerRect: ScrollRect,
  popupRect: ScrollRect,
  {
    customStyle = {},
  }: {
    customStyle?: CSSProperties;
  },
): CSSProperties => {
  if (["top", "tl", "tr", "bottom", "bl", "br"].includes(position)) {
    let offsetLeft = Math.abs(
      triggerRect.left + triggerRect.width / 2 - popupRect.left,
    );

    if (offsetLeft > popupRect.width - 8) {
      if (triggerRect.width > popupRect.width) {
        offsetLeft = popupRect.width / 2;
      } else {
        offsetLeft = popupRect.width - 8;
      }
    }

    if (["top", "tl", "tr"].includes(position)) {
      return {
        left: `${offsetLeft}px`,
        bottom: "0",
        transform: "translate(-50%,50%) rotate(45deg)",
        ...customStyle,
      };
    }
    return {
      left: `${offsetLeft}px`,
      top: "0",
      transform: "translate(-50%,-50%) rotate(45deg)",
      ...customStyle,
    };
  }
  let offsetTop = Math.abs(
    triggerRect.top + triggerRect.height / 2 - popupRect.top,
  );

  if (offsetTop > popupRect.height - 8) {
    if (triggerRect.height > popupRect.height) {
      offsetTop = popupRect.height / 2;
    } else {
      offsetTop = popupRect.height - 8;
    }
  }

  if (["left", "lt", "lb"].includes(position)) {
    return {
      top: `${offsetTop}px`,
      right: "0",
      transform: "translate(50%,-50%) rotate(45deg)",
      ...customStyle,
    };
  }
  return {
    top: `${offsetTop}px`,
    left: "0",
    transform: "translate(-50%,-50%) rotate(45deg)",
    ...customStyle,
  };
};
